%{
#include <cstdlib>
#include <cstdio>
#include <string>
#include <iostream>
#include <map>
#include <vector>
#include <sstream>

#define YYSTYPE ATRIBUTOS  //tipo de retorno qnd um token eh encontrado

using namespace std;

struct TIPO {
  string base; //indica qual o tipo da variavel
  int d1, d2; //dimensões. Usados num vetor/matriz

  TIPO (): base(""), d1(0), d2(0){}

  TIPO (string base, int d1 = 0, int d2 = 0):
   base(base), d1(d1), d2(d2){}  
};

struct VARTEMP {
  int i, d, b, c, s;

  VARTEMP (): i(0), d(0), b(0), c(0), s(0){}
};

bool operator == (const TIPO &a, const TIPO &b){
    return a.base == b.base &&
	   a.d1 == b.d1 &&
	   a.d2 == b.d2;
}

bool operator != (const TIPO& a, const TIPO& b){
    return !(a == b);
}

struct ATRIBUTOS {
  string c, v; //código e valor
  TIPO t;

  ATRIBUTOS() : c(), v(), t() {}
  ATRIBUTOS(string v, const TIPO& t) : c(), v(v), t(t) {}
};

struct FUNCAO {
  string id;
  TIPO retorno;
  
  vector<TIPO> params;

  FUNCAO() : id(), retorno() {}
  FUNCAO(string id, TIPO tipo) : id(id), retorno(tipo) {}
};

int yylex();
int yyparse();

void yyerror( const char* st );
void erro( string msg );
void erroSemantico( string msg );

string geratemp(string tipo);
void zeraAtributos(ATRIBUTOS &);

void rastreiaToken( char const *lexema, char const *tipo, char const *regra );
void verificaVariavel(ATRIBUTOS &);

void atribuiVariavel(ATRIBUTOS &, ATRIBUTOS, ATRIBUTOS);
void atribuiVariavelVetor(ATRIBUTOS &, ATRIBUTOS, ATRIBUTOS, ATRIBUTOS);
void atribuiVariavelVetor2(ATRIBUTOS &, ATRIBUTOS, ATRIBUTOS, ATRIBUTOS, ATRIBUTOS);

void insereFuncao(string id, TIPO retorno);
void insereVariavelFuncao(string funcao, string id, TIPO tipo);
void verificaFuncao(string id);

void geraCodigo(ATRIBUTOS &ss, ATRIBUTOS s1, string opr, ATRIBUTOS s2);
string buscaTipoOpr(string opr, string t1, string t2);

void insereVariavel(string id, TIPO &tipo);
void insereVariavelVetor(string id, string d1, TIPO &tipo);
void insereVariavelVetor2(string id, string d1, string d2, TIPO &tipo);

string geraTemporarias();
string geraLabel(string opr);
string geraCodigoVariavel(string, TIPO);
string geraCodigoVariavelLocal(string, TIPO);
void geraCodigoChamadaFuncao(ATRIBUTOS &ss, ATRIBUTOS id, ATRIBUTOS args);
void geraCodigoVetor(ATRIBUTOS &ss, ATRIBUTOS id, ATRIBUTOS offset);
void geraCodigoVetor2(ATRIBUTOS &ss, ATRIBUTOS id, ATRIBUTOS offset1, ATRIBUTOS offset2);
void geraCodigoIf(ATRIBUTOS &ss, ATRIBUTOS e, ATRIBUTOS cmds);
void geraCodigoIfElse(ATRIBUTOS &ss, ATRIBUTOS e, ATRIBUTOS cmds_t, ATRIBUTOS cmds_f);
void geraCodigoWhile(ATRIBUTOS &ss, ATRIBUTOS e, ATRIBUTOS cmds);
void geraCodigoFor(ATRIBUTOS &ss, ATRIBUTOS var, ATRIBUTOS cond, ATRIBUTOS passo, ATRIBUTOS cmds);
void geraCodigoCin(ATRIBUTOS &ss, ATRIBUTOS v);
void geraCodigoCout(ATRIBUTOS &ss, ATRIBUTOS v);

void geraCodigoFuncao(ATRIBUTOS &, ATRIBUTOS, ATRIBUTOS, TIPO);
string tipoToStr( string tipo );
void insereNomeFuncao(string id);
void alteraTipoFuncao(string fn, TIPO tipo);
void insereVariavelLocal(string fn, string id, TIPO &tipo);
void insereVariavelVetorLocal(string fn, string id, string d1, TIPO &tipo);
void insereVariavelVetor2Local(string fn, string id, string d1, string d2, TIPO &tipo);
void zeraLocais();
void zeraTemporarias();

int toInt(string n);
string toStr(int n);
void trocaAspasSimples(string&);

void geraCodigoReturn(ATRIBUTOS &, ATRIBUTOS);

VARTEMP varTemp = VARTEMP();
string ultimaFn = "";
bool emFuncao = false;

%}

%token _CTE_INT _CTE_BOOL _CTE_FLOAT _CTE_CHAR _CTE_STRING
%token _INTEIRO _STRING _FLOAT _CHAR _BOOL
%token _ABREPARENTESE _FECHAPARENTESE _ABRECHAVE _FECHACHAVE _COMECA _FIM
%token _VERDADE _CONTRARIO
%token _IMPLICA
%token _CASO _SEJA _FIMCOMANDO _SEMPRE _QUE _SOMATORIO _DE _ENQUANTO _PASSO
%token _ELEMENTOS _ALGORITMO _FUNCOES
%token _ATRIB _VIRGULA _AND _OR _NOT _DIF _MAIG _MEIG _MAIOR _MENOR _IGUAL _MAIS _MENOS _MULTIPLICACAO _DIVISAO _RESTO
%token _ID _CIN _COUT _RETURN

%left _OR
%left _AND
%nonassoc _MENOR _MAIOR _IGUAL _DIF _MAIG _MEIG
%left _MAIS _MENOS
%left _MULTIPLICACAO _DIVISAO _RESTO

%%
S
  : DECLS FUNCOES BLOCO_PRINCIPAL {
      cout << endl;
      cout << "#include <stdio.h>" << endl;
      cout << "#include <string.h>" << endl;
      cout << "#include <iostream>" << endl;
      cout << "using namespace std;" << endl;
      cout << $1.c << endl;
      cout << $2.c << endl;
      cout << $3.c << endl;
      }
  ;

DECLS
  :_ELEMENTOS DECLSC { $$.c = $2.c; }
  | { zeraAtributos($$); }
  ;

DECLSC
  :_ID _ABRECHAVE _CTE_INT _FECHACHAVE TIPO _FIMCOMANDO DECLSC { 
	insereVariavelVetor($1.v, $3.v, $5.t);
	$$.t = $5.t;
	$$.c = $7.c + geraCodigoVariavel($1.v, $5.t);
  }
  |_ID _ABRECHAVE _CTE_INT _FECHACHAVE _ABRECHAVE _CTE_INT _FECHACHAVE TIPO _FIMCOMANDO DECLSC {
	insereVariavelVetor2($1.v, $3.v, $6.v, $8.t);
	$$.t = $8.t;
	$$.c = $10.c + geraCodigoVariavel($1.v, $8.t);
  }
  |_ID _ABRECHAVE _CTE_INT _VIRGULA _CTE_INT _FECHACHAVE TIPO _FIMCOMANDO DECLSC {
	insereVariavelVetor2($1.v, $3.v, $5.v, $7.t);
	$$.t = $7.t;
	$$.c = $9.c + geraCodigoVariavel($1.v, $7.t);
  }
  |_ID TIPO _FIMCOMANDO DECLSC {
	insereVariavel($1.v, $2.t);
	$$.t = $2.t;
	$$.c = $4.c + geraCodigoVariavel($1.v, $2.t);
  }
  |_ID _ABRECHAVE _CTE_INT _FECHACHAVE _VIRGULA DECLSC {
	insereVariavelVetor($1.v, $3.v, $6.t);
	$$.t = $6.t;
	$$.c = $6.c + geraCodigoVariavel($1.v, $6.t);
  }
  |_ID _ABRECHAVE _CTE_INT _FECHACHAVE _ABRECHAVE _CTE_INT _FECHACHAVE _VIRGULA DECLSC {
	insereVariavelVetor2($1.v, $3.v, $6.v, $9.t);
	$$.t = $9.t;
	$$.c = $9.c + geraCodigoVariavel($1.v, $9.t);
  }
  |_ID _ABRECHAVE _CTE_INT _VIRGULA _CTE_INT _FECHACHAVE _VIRGULA DECLSC {
	insereVariavelVetor2($1.v, $3.v, $5.v, $8.t);
	$$.t = $8.t;
	$$.c = $8.c + geraCodigoVariavel($1.v, $8.t);
  }
  |_ID _VIRGULA DECLSC {
	insereVariavel($1.v, $3.t);
	$$.t = $3.t;
	$$.c = $3.c + geraCodigoVariavel($1.v, $3.t);
  }
  |{ zeraAtributos($$); }
  ;

TIPO
  : _INTEIRO { $$ = $1; }
  | _FLOAT { $$ = $1; }
  | _STRING { $$ = $1; }
  | _BOOL { $$ = $1; }
  | _CHAR { $$ = $1; }
  ;

BLOCO_PRINCIPAL
  :_ALGORITMO _COMECA CMDS _FIM { $$.c = "int main() {\n" + geraTemporarias() + $3.c + " \nreturn 0; \n}"; }
  ;

FUNCOES
  :_FUNCOES DECLFUNCOES { $$.c = $2.c; }
  |{ zeraAtributos($$); }
  ; 

NOMEFUN
  : _ID { insereNomeFuncao($1.v); $$.v = $1.v; }
  ;

CABECFUN
  : NOMEFUN _ABREPARENTESE DECLSF _FECHAPARENTESE _IMPLICA TIPO DECLF _COMECA {
	alteraTipoFuncao($1.v, $6.t);
	geraCodigoFuncao($$, $1, $3, $6.t);
	emFuncao = true;
	$$.c += $7.c;
  }
  | NOMEFUN _ABREPARENTESE DECLSF _FECHAPARENTESE DECLF _COMECA {
	alteraTipoFuncao($1.v, TIPO());
	geraCodigoFuncao($$, $1, $3, TIPO());
	emFuncao = true;
	$$.c += $5.c;
  }
  ;

COD_FUN
  : CABECFUN CMDS{
	$$.c = $1.c + 
	       geraTemporarias() + 
	       $2.c;
        emFuncao = false;
      	zeraLocais();
  }

  ;
DECLFUNCOES
  : COD_FUN _FIM DECLFUNCOES { $$.c = $1.c + "}\n" + $3.c; }
  |{ zeraAtributos($$); }
  ;

DECLF
  :_ELEMENTOS DECLPF { $$.c = $2.c; }
  | { zeraAtributos($$); }
  ;

DECLPF
  :_ID _ABRECHAVE _CTE_INT _FECHACHAVE TIPO _FIMCOMANDO DECLPF { 
	insereVariavelVetorLocal(ultimaFn, $1.v, $3.v, $5.t);
	$$.t = $5.t;
	$$.c = geraCodigoVariavelLocal($1.v, $5.t) + ";\n" + $7.c;
  }
  |_ID _ABRECHAVE _CTE_INT _FECHACHAVE _ABRECHAVE _CTE_INT _FECHACHAVE TIPO _FIMCOMANDO DECLPF {
	insereVariavelVetor2Local(ultimaFn, $1.v, $3.v, $6.v, $8.t);
	$$.t = $8.t;
	$$.c = geraCodigoVariavelLocal($1.v, $8.t) + ";\n" + $10.c;
  }
  |_ID _ABRECHAVE _CTE_INT _VIRGULA _CTE_INT _FECHACHAVE TIPO _FIMCOMANDO DECLPF {
	insereVariavelVetor2Local(ultimaFn, $1.v, $3.v, $5.v, $7.t);
	$$.t = $7.t;
	$$.c = geraCodigoVariavelLocal($1.v, $7.t)  + ";\n" + $9.c;
  }
  |_ID TIPO _FIMCOMANDO DECLPF {
	insereVariavelLocal(ultimaFn, $1.v, $2.t);
	$$.t = $2.t;
	$$.c = geraCodigoVariavelLocal($1.v, $2.t) + ";\n" + $4.c;
  }
  |_ID _ABRECHAVE _CTE_INT _FECHACHAVE _VIRGULA DECLPF {
	insereVariavelVetorLocal(ultimaFn, $1.v, $3.v, $5.t);
	$$.t = $5.t;
	$$.c = geraCodigoVariavelLocal($1.v, $5.t);
  }
  |_ID _ABRECHAVE _CTE_INT _FECHACHAVE _ABRECHAVE _CTE_INT _FECHACHAVE _VIRGULA DECLPF {
	insereVariavelVetor2Local(ultimaFn, $1.v, $3.v, $6.v, $8.t);
	$$.t = $8.t;
	$$.c = geraCodigoVariavelLocal($1.v, $8.t);
  }
  |_ID _ABRECHAVE _CTE_INT _VIRGULA _CTE_INT _FECHACHAVE _VIRGULA DECLPF {
	insereVariavelVetor2Local(ultimaFn, $1.v, $3.v, $5.v, $7.t);
	$$.t = $7.t;
	$$.c = geraCodigoVariavelLocal($1.v, $7.t);
  }
  |_ID _VIRGULA DECLPF {
	insereVariavelLocal(ultimaFn, $1.v, $2.t);
	$$.t = $2.t;
	$$.c = geraCodigoVariavelLocal($1.v, $2.t);
  }
  |{ zeraAtributos($$); }
  ;

DECLSF
  :_ID _ABRECHAVE _CTE_INT _FECHACHAVE TIPO _VIRGULA DECLSF { 
	insereVariavelVetorLocal(ultimaFn, $1.v, $3.v, $5.t);
	$$.t = $5.t;
	$$.c = geraCodigoVariavelLocal($1.v, $5.t) + ", " + $7.c;
  }
  |_ID _ABRECHAVE _CTE_INT _FECHACHAVE _ABRECHAVE _CTE_INT _FECHACHAVE TIPO _VIRGULA DECLSF {
	insereVariavelVetor2Local(ultimaFn, $1.v, $3.v, $6.v, $8.t);
	$$.t = $8.t;
	$$.c = geraCodigoVariavelLocal($1.v, $8.t) + ", " + $10.c;
  }
  |_ID _ABRECHAVE _CTE_INT _VIRGULA _CTE_INT _FECHACHAVE TIPO _VIRGULA DECLSF {
	insereVariavelVetor2Local(ultimaFn, $1.v, $3.v, $5.v, $7.t);
	$$.t = $7.t;
	$$.c = geraCodigoVariavelLocal($1.v, $7.t)  + ", " + $9.c;
  }
  |_ID TIPO _VIRGULA DECLSF {
	insereVariavelLocal(ultimaFn, $1.v, $2.t);
	$$.t = $2.t;
	$$.c = geraCodigoVariavelLocal($1.v, $2.t) + ", " + $4.c;
  }
  |_ID _ABRECHAVE _CTE_INT _FECHACHAVE TIPO { 
	insereVariavelVetorLocal(ultimaFn, $1.v, $3.v, $5.t);
	$$.t = $5.t;
	$$.c = geraCodigoVariavelLocal($1.v, $5.t);
  }
  |_ID _ABRECHAVE _CTE_INT _FECHACHAVE _ABRECHAVE _CTE_INT _FECHACHAVE TIPO {
	insereVariavelVetor2Local(ultimaFn, $1.v, $3.v, $6.v, $8.t);
	$$.t = $8.t;
	$$.c = geraCodigoVariavelLocal($1.v, $8.t);
  }
  |_ID _ABRECHAVE _CTE_INT _VIRGULA _CTE_INT _FECHACHAVE TIPO {
	insereVariavelVetor2Local(ultimaFn, $1.v, $3.v, $5.v, $7.t);
	$$.t = $7.t;
	$$.c = geraCodigoVariavelLocal($1.v, $7.t);
  }
  |_ID TIPO {
	insereVariavelLocal(ultimaFn, $1.v, $2.t);
	$$.t = $2.t;
	$$.c = geraCodigoVariavelLocal($1.v, $2.t);
  }
  |{ zeraAtributos($$); }
  ;

CMDS 
  : CMD { $$.c = $1.c; }
  | CMD CMDS { $$.c = $1.c + $2.c; }
  ;

CMD
  : COM_IO	{ $$.c = $1.c; }
  | COM_SE	{ $$.c = $1.c; }
  | CMD_ATRIB	{ $$.c = $1.c; }
  | COM_WHILE   { $$.c = $1.c; }
  | COM_FOR	{ $$.c = $1.c; } 
  | COM_RET	{ $$.c = $1.c; }
  | CHAMADA_FUNCAO _FIMCOMANDO { $$ = $1; }
  ;

COM_RET
  : _RETURN E _FIMCOMANDO { geraCodigoReturn($$, $2); }
  ;

COM_IO
  : _CIN G _FIMCOMANDO { geraCodigoCin($$, $2); }
  | _COUT COM_COUT { $$.c = $2.c; }
  ;

COM_COUT
  : G _VIRGULA COM_COUT { $$.c = $1.c + "cout << " + $1.v + ";\n" + $3.c; }
  | CTE _VIRGULA COM_COUT { $$.c = $1.c + "cout << " + $1.v + ";\n" + $3.c; }
  | G _FIMCOMANDO { $$.c = $1.c + "cout << " + $1.v + ";\ncout << endl;\n"; }
  | CTE _FIMCOMANDO { $$.c = $1.c + "cout << " + $1.v + ";\ncout << endl;\n"; }
  ;

COM_SE
  : _CASO E _SEJA _VERDADE _COMECA CMDS _FIM _FIMCOMANDO { geraCodigoIf($$, $2, $6); }
  | _CASO E _SEJA _VERDADE _COMECA CMDS _FIM _CASO _CONTRARIO _COMECA CMDS _FIM _FIMCOMANDO{ geraCodigoIfElse($$, $2, $6, $11); }
  ;

COM_WHILE
  : _SEMPRE _QUE E _COMECA CMDS _FIM _FIMCOMANDO { geraCodigoWhile($$, $3, $5); }
  ;

COM_FOR
  : _SOMATORIO _DE _ID _ATRIB E _ENQUANTO E _DE _PASSO E _COMECA CMDS _FIM _FIMCOMANDO {
      verificaVariavel($3);
      atribuiVariavel($$, $3, $5);
      geraCodigoFor($$, $3, $7, $10, $12);
    }
  ;

CMD_ATRIB 
  : _ID _ATRIB E _FIMCOMANDO { verificaVariavel($1); atribuiVariavel($$, $1, $3); }
  | _ID _ABRECHAVE E _FECHACHAVE _ATRIB E _FIMCOMANDO { verificaVariavel($1); atribuiVariavelVetor($$, $1, $3, $6); }
  | _ID _ABRECHAVE E _FECHACHAVE _ABRECHAVE E _FECHACHAVE _ATRIB E _FIMCOMANDO { verificaVariavel($1); atribuiVariavelVetor2($$, $1, $3, $6, $9); }
  | _ID _ABRECHAVE E _VIRGULA E _FECHACHAVE _ATRIB E _FIMCOMANDO { verificaVariavel($1); atribuiVariavelVetor2($$, $1, $3, $5, $8); }
  ;

E 
  : E _OR E { geraCodigo($$, $1, "||", $3); }
  | E _AND E { geraCodigo($$, $1, "&&", $3); }
  | E _MENOR E { geraCodigo($$, $1, $2.v, $3); }
  | E _MAIOR E { geraCodigo($$, $1, $2.v, $3); }
  | E _IGUAL E { geraCodigo($$, $1, $2.v, $3); }
  | E _DIF E { geraCodigo($$, $1, $2.v, $3); }
  | E _MAIG E { geraCodigo($$, $1, $2.v, $3); }
  | E _MEIG E { geraCodigo($$, $1, $2.v, $3); }
  | E _MENOS E { geraCodigo($$, $1, $2.v, $3); }
  | E _MULTIPLICACAO E { geraCodigo($$, $1, $2.v, $3); }
  | E _DIVISAO E { geraCodigo($$, $1, $2.v, $3); }
  | E _RESTO E { geraCodigo($$, $1, $2.v, $3); }
  | E _MAIS E { geraCodigo($$, $1, $2.v, $3); }
  | F { $$ = $1; }
  ;


F 
  : _ABREPARENTESE E _FECHAPARENTESE { $$ = $2; } 
  | _NOT F { $$.c = $2.c; $$.v = "!" + $2.v; $$.t = $2.t; }
  | G { $$ = $1; }
  | CTE { $$ = $1; }
  ;

G
  : _ID { verificaVariavel($1); $$ = $1; }
  | _ID _ABRECHAVE E _FECHACHAVE { verificaVariavel($1); geraCodigoVetor($$, $1, $3); }
  | _ID _ABRECHAVE E _FECHACHAVE _ABRECHAVE E _FECHACHAVE { verificaVariavel($1); $$ = $1; geraCodigoVetor2($$, $1, $3, $6); }
  | _ID _ABRECHAVE E _VIRGULA E _FECHACHAVE { verificaVariavel($1); $$ = $1; geraCodigoVetor2($$, $1, $3, $5); }
  | CHAMADA_FUNCAO { $$ = $1; }
  ;

CHAMADA_FUNCAO
  : _ID _ABREPARENTESE ARGS _FECHAPARENTESE {
	  verificaFuncao($1.v);
	  geraCodigoChamadaFuncao($$, $1, $3);
    }
  ;

ARGS : E _VIRGULA ARGS 
       { $$.c = $1.c + $3.c;
         $$.v = $1.v + ", " + $3.v; }
     | E { $$.c = $1.c; $$.v = $1.v; }
     ;

CTE
  : _CTE_INT { $$=$1; }
  | _CTE_FLOAT { $$=$1; }
  | _CTE_BOOL { $$=$1;}
  | _CTE_STRING { $$=$1; trocaAspasSimples($$.v); }
  | _CTE_CHAR { $$=$1; }
  ;
  
%%

//inicialização de variaveis
int numLinha = 1;
int numCol = 0;
string ultimoToken = "";
string ultimaLinha = "";
string ultimaRegra = "";

map<string, TIPO> tabelaSimbolos;
map<string, TIPO> tabelaSimbolosLocais;
map<string, string> tabelaResultados;
map<string, FUNCAO> tabelaFuncoes;

void initTabelaResultados(){
  //Soma
  tabelaResultados["I+I"] = "I";
  tabelaResultados["I+D"] = "D";
  tabelaResultados["I+C"] = "I";
  
  tabelaResultados["D+I"] = "D";
  tabelaResultados["D+D"] = "D";

  tabelaResultados["C+I"] = "I";
  tabelaResultados["C+C"] = "C";

  tabelaResultados["S+C"] = "S";
  tabelaResultados["S+S"] = "S";

  //Subtração
  tabelaResultados["I-I"] = "I";
  tabelaResultados["I-D"] = "D";
  tabelaResultados["I-C"] = "I";

  tabelaResultados["D-I"] = "D";
  tabelaResultados["D-D"] = "D";

  tabelaResultados["C-I"] = "I";
  tabelaResultados["C-C"] = "C";

  //Multiplicacao
  tabelaResultados["I*I"] = "I";
  tabelaResultados["I*D"] = "D";
  tabelaResultados["I*C"] = "I";

  tabelaResultados["D*I"] = "D";
  tabelaResultados["D*D"] = "D";

  tabelaResultados["C*I"] = "I";
  tabelaResultados["C*C"] = "C";

  //Divisao
  tabelaResultados["I/I"] = "I";
  tabelaResultados["I/D"] = "D";
  tabelaResultados["I/C"] = "I";

  tabelaResultados["D/I"] = "D";
  tabelaResultados["D/D"] = "D";

  tabelaResultados["C/I"] = "I";
  tabelaResultados["C/C"] = "C";

  //Modulo
  tabelaResultados["I%I"] = "I";
  tabelaResultados["I%C"] = "I";

  tabelaResultados["C%I"] = "I";
  tabelaResultados["C%C"] = "C";

  //Pow
  tabelaResultados["I**I"] = "I";
  tabelaResultados["I**C"] = "I";

  tabelaResultados["D**I"] = "D";
  tabelaResultados["D**C"] = "D";

  tabelaResultados["C**I"] = "I";
  tabelaResultados["C**C"] = "C";

  //Menor
  tabelaResultados["I<I"] = "B";
  tabelaResultados["I<D"] = "B";
  tabelaResultados["I<C"] = "B";

  tabelaResultados["D<I"] = "B";
  tabelaResultados["D<D"] = "B";

  tabelaResultados["B<B"] = "B";

  tabelaResultados["C<I"] = "B";
  tabelaResultados["C<C"] = "B";

  tabelaResultados["S<S"] = "B";

  //MenorIgual
  tabelaResultados["I<=I"] = "B";
  tabelaResultados["I<=D"] = "B";
  tabelaResultados["I<=C"] = "B";

  tabelaResultados["D<=I"] = "B";
  tabelaResultados["D<=D"] = "B";

  tabelaResultados["B<=B"] = "B";

  tabelaResultados["C<=I"] = "B";
  tabelaResultados["C<=C"] = "B";

  tabelaResultados["S<=S"] = "B";

  //Maior
  tabelaResultados["I>I"] = "B";
  tabelaResultados["I>D"] = "B";
  tabelaResultados["I>C"] = "B";

  tabelaResultados["D>I"] = "B";
  tabelaResultados["D>D"] = "B";

  tabelaResultados["B>B"] = "B";

  tabelaResultados["C>I"] = "B";
  tabelaResultados["C>C"] = "B";

  tabelaResultados["S>S"] = "B";

  //MaiorIgual
  tabelaResultados["I>=I"] = "B";
  tabelaResultados["I>=D"] = "B";
  tabelaResultados["I>=C"] = "B";

  tabelaResultados["D>=I"] = "B";
  tabelaResultados["D>=D"] = "B";

  tabelaResultados["B>=B"] = "B";

  tabelaResultados["C>=I"] = "B";
  tabelaResultados["C>=C"] = "B";

  tabelaResultados["S>=S"] = "B";

  //Igual
  tabelaResultados["I=I"] = "B";
  tabelaResultados["I=D"] = "B";
  tabelaResultados["I=C"] = "B";

  tabelaResultados["D=I"] = "B";
  tabelaResultados["D=D"] = "B";

  tabelaResultados["B=B"] = "B";

  tabelaResultados["C=I"] = "B";
  tabelaResultados["C=C"] = "B";

  tabelaResultados["S=S"] = "B";

  //Diferente
  tabelaResultados["I~=I"] = "B";
  tabelaResultados["I~=D"] = "B";
  tabelaResultados["I~=C"] = "B";

  tabelaResultados["D~=I"] = "B";
  tabelaResultados["D~=D"] = "B";

  tabelaResultados["B~=B"] = "B";

  tabelaResultados["C~=I"] = "B";
  tabelaResultados["C~=C"] = "B";

  tabelaResultados["S~=S"] = "B";

  //And
  tabelaResultados["B&&B"] = "B";
  tabelaResultados["I&&I"] = "B";

  //Or
  tabelaResultados["B||B"] = "B";
  tabelaResultados["I||I"] = "B";

  //Atribuicao
  tabelaResultados["I:=I"] = "I";
  tabelaResultados["I:=B"] = "I";
  tabelaResultados["I:=C"] = "I";

  tabelaResultados["D:=I"] = "D";
  tabelaResultados["D:=D"] = "D";
  tabelaResultados["D:=B"] = "D";
  tabelaResultados["D:=C"] = "D";

  tabelaResultados["B:=I"] = "B";
  tabelaResultados["B:=B"] = "B";

  tabelaResultados["C:=I"] = "C";
  tabelaResultados["C:=C"] = "C";

  tabelaResultados["S:=I"] = "S";
  tabelaResultados["S:=D"] = "S";
  tabelaResultados["S:=B"] = "S";
  tabelaResultados["S:=C"] = "S";
  tabelaResultados["S:=S"] = "S";
}

#include "lex.yy.c"

void rastreiaToken( char const *lexema, char const *tipo, char const *regra) {
  numCol += strlen(lexema);  
  ultimoToken = lexema;
  ultimaLinha += " "+ultimoToken;
  ultimaRegra = regra;

  yylval.c = "";
  yylval.v = lexema;
  yylval.t = TIPO(tipo);
}

void insereNomeFuncao(string id){
  ultimaFn = id;

  if( tabelaFuncoes.find(id) != tabelaFuncoes.end() )
    erroSemantico("Funcao já declarada: " + id);
}

void insereVariavel(string id, TIPO &tipo){
  if (emFuncao){
    if( tabelaSimbolosLocais.find(id) != tabelaSimbolosLocais.end() )
      erroSemantico("Variavel já declarada: " + id);
    
    tipo.d1 = 0;
    tipo.d2 = 0;
    tabelaSimbolosLocais[id] = tipo;
  } else {
    if( tabelaSimbolos.find(id) != tabelaSimbolos.end() )
      erroSemantico("Variavel já declarada: " + id);
    
    tipo.d1 = 0;
    tipo.d2 = 0;
    tabelaSimbolos[id] = tipo;
  }
}

void insereVariavelVetor(string id, string d1, TIPO &tipo){
  if (emFuncao){
    if( tabelaSimbolosLocais.find(id) != tabelaSimbolosLocais.end() )
      erroSemantico("Variavel já declarada: " + id);

    tipo.d1 = toInt(d1);
    tipo.d2 = 0;
    tabelaSimbolosLocais[id] = tipo;
  } else {
    if( tabelaSimbolos.find(id) != tabelaSimbolos.end() )
      erroSemantico("Variavel já declarada: " + id);

    tipo.d1 = toInt(d1);
    tipo.d2 = 0;
    tabelaSimbolos[id] = tipo;
  }
}

void insereVariavelVetor2(string id, string d1, string d2, TIPO &tipo){
  if (emFuncao){
    if( tabelaSimbolosLocais.find(id) != tabelaSimbolosLocais.end() )
      erroSemantico("Variavel já declarada: " + id);

    tipo.d1 = toInt(d1);
    tipo.d2 = toInt(d2);
    tabelaSimbolosLocais[id] = tipo;
  } else {
    if( tabelaSimbolos.find(id) != tabelaSimbolos.end() )
      erroSemantico("Variavel já declarada: " + id);

    tipo.d1 = toInt(d1);
    tipo.d2 = toInt(d2);
    tabelaSimbolos[id] = tipo;
  }
}

void insereVariavelLocal(string fn, string id, TIPO &tipo){
  if( tabelaSimbolosLocais.find(id) != tabelaSimbolosLocais.end() )
    erroSemantico("Variavel já declarada: " + id);
  
  tipo.d1 = 0;
  tipo.d2 = 0;
  tabelaSimbolosLocais[id] = tipo;

  tabelaFuncoes[fn].params.push_back(tipo);
}

void insereVariavelVetorLocal(string fn, string id, string d1, TIPO &tipo){
  if( tabelaSimbolosLocais.find(id) != tabelaSimbolosLocais.end() )
    erroSemantico("Variavel já declarada: " + id);

  tipo.d1 = toInt(d1);
  tipo.d2 = 0;
  tabelaSimbolosLocais[id] = tipo;

  tabelaFuncoes[fn].params.push_back(tipo);
}

void insereVariavelVetor2Local(string fn, string id, string d1, string d2, TIPO &tipo){
  if( tabelaSimbolosLocais.find(id) != tabelaSimbolosLocais.end() )
    erroSemantico("Variavel já declarada: " + id);

  tipo.d1 = toInt(d1);
  tipo.d2 = toInt(d2);
  tabelaSimbolosLocais[id] = tipo;

  tabelaFuncoes[fn].params.push_back(tipo);
}

void alteraTipoFuncao(string fn, TIPO tipo){
  tabelaFuncoes[fn].retorno = tipo;
}

void zeraLocais(){
  tabelaSimbolosLocais.clear();
}

void zeraTemporarias(){
  varTemp = VARTEMP();
}

void atribuiVariavel(ATRIBUTOS &ss, ATRIBUTOS s1, ATRIBUTOS s3){
  
  string tipo = tabelaResultados[s1.t.base + ":=" + s3.t.base];
  if (tipo == "")
    erroSemantico("Não é possível atribuir " + s3.t.base + " a uma varíavel " + s1.t.base);

  ss.v = "";
  if (s1.t.base == "S"){
    ss.c = s3.c + "strncpy(" + s1.v + ", " + s3.v + ", 255);\n";
  } else {
    ss.c = s3.c + s1.v + " = " + s3.v + ";\n";
  }
}

void atribuiVariavelVetor(ATRIBUTOS &ss, ATRIBUTOS id, ATRIBUTOS offset, ATRIBUTOS resul){
  if (id.t.d1 == 0)
    erroSemantico("Variavel " + id.v + " não é um vetor");

  if (id.t.d2 != 0)
    erroSemantico("Variavel " + id.v + " não é uma vetor. Indexe com duas dimensões");

  if (offset.t.base != "I")
    erroSemantico("Deslocamento de um vetor deve ser um Inteiro");
  
  ss.v = "";
  if (id.t.base == "S"){
    
    string temp_offset = geratemp("I");

    ss.c = temp_offset + " = " + offset.v + " * 255;\n" + 
	offset.c + resul.c + "strncpy(" + id.v + "[" + temp_offset + "], " + resul.v + ", 255);\n";
  } else {
    ss.c = offset.c + resul.c + id.v + "[" + offset.v + "] = " + resul.v + ";\n"; 
  }
}

void atribuiVariavelVetor2(ATRIBUTOS &ss, ATRIBUTOS id, ATRIBUTOS offset1, ATRIBUTOS offset2, ATRIBUTOS resul){
  if (id.t.d1 == 0 || id.t.d2 == 0)
    erroSemantico("Variavel " + id.v + " não é uma matriz");

  if (offset1.t.base != "I" || offset2.t.base != "I")
    erroSemantico("Deslocamento de uma matriz deve ser um Inteiro");

  string temp_offset = geratemp("I");
  string temp_offset2 = geratemp("I");

  ss.v = "";
  if (id.t.base == "S"){
    ss.c = offset1.c + offset2.c + resul.c + 
	temp_offset + " = " + offset1.v + " * " + toStr(id.t.d1) + ";\n" +
	temp_offset + " = " + temp_offset + " * 255;\n" +
	temp_offset2 + " = " + offset2.v + " * 255;\n" + 
	temp_offset + " = " + temp_offset + " + " + temp_offset2 + ";\n" +
	"strncpy(" + id.v + "[" + temp_offset + "], " + resul.v + ", 255);\n";
  } else {
    ss.c = offset1.c + offset2.c + resul.c + 
	temp_offset + " = " + offset1.v + " * " + toStr(id.t.d1) + ";\n" +
	temp_offset + " = " + temp_offset + " + " + offset2.v + ";\n" +
	id.v + "[" + temp_offset + "] = " + resul.v + ";\n";
  }
}

int toInt( string n ) {
  int aux = 0;
  
  if( sscanf( n.c_str(), "%d", &aux ) != 1 )
    erro("BUG!!! Nao conseguiu converter " + n);

  return aux;
}

string toStr(int n) {
  stringstream ss;
  ss << n;
  return ss.str();
}

void trocaAspasSimples( string& str ) {
  if (str[0] == '\'')
    str[0] = '\"';
  if (str[str.size()-1] == '\'')
    str[str.size()-1] = '\"';
}

void verificaVariavel(ATRIBUTOS &ss){
  if( tabelaSimbolosLocais.find(ss.v) == tabelaSimbolosLocais.end() )
    if( tabelaSimbolos.find(ss.v) == tabelaSimbolos.end() )
      erroSemantico("Variavel não declarada: " + ss.v);
    else
      ss.t = tabelaSimbolos[ss.v];
  else
    ss.t = tabelaSimbolosLocais[ss.v];
}

void verificaFuncao(string id){
  if( tabelaFuncoes.find(id) == tabelaFuncoes.end() )
    erroSemantico("Funcao não declarada: " + id);
}

string geratemp(string tipo) {
  char buf[200];
  TIPO tp = TIPO(tipo);

  if (tipo == "I"){
    sprintf( buf, "ti_%03d", varTemp.i++ );
  } else if (tipo == "D"){
    sprintf( buf, "td_%03d", varTemp.d++ );
  } else if (tipo == "B"){
    sprintf( buf, "tb_%03d", varTemp.b++ );
  } else if (tipo == "C"){
    sprintf( buf, "tc_%03d", varTemp.c++ );
  } else if (tipo == "S"){
    sprintf( buf, "ts_%03d", varTemp.s++ );
  } else {
    erro("BUG!! Tipo não existente");
  }

  insereVariavel(buf, tp);

  return buf;
}

string geraLabel(string opr){
  static int numLabel = 0;

  return "lbl_" + opr + "_" + toStr( ++numLabel );
}

string geraTemporarias(){
  int i;
  char buf[200];

  stringstream aux;

  for(int i=0; i<varTemp.i; i++){
    sprintf( buf, "ti_%03d", i );
    aux << "int " << buf << ";\n";
  }
  for(int i=0; i<varTemp.d; i++){
    sprintf( buf, "td_%03d", i );
    aux << "float " << buf << ";\n";
  }
  for(int i=0; i<varTemp.b; i++){
    sprintf( buf, "tb_%03d", i );
    aux << "int " << buf << ";\n";
  }
  for(int i=0; i<varTemp.c; i++){
    sprintf( buf, "tc_%03d", i );
    aux << "char " << buf << ";\n";
  }
  for(int i=0; i<varTemp.s; i++){
    sprintf( buf, "ts_%03d", i );
    aux << "char " << buf << "[255];\n";
  }


  zeraTemporarias();
  return aux.str();
}

void geraCodigoChamadaFuncao(ATRIBUTOS &ss, ATRIBUTOS id, ATRIBUTOS args){
  ss.t = tabelaFuncoes[id.v].retorno;

  if (ss.t.base == ""){ //funcao void
    ss.c = args.c + id.v + "(" + args.v + ");\n";
  } else {
    string temp = geratemp(ss.t.base);

    ss.c = args.c + temp + " = " + id.v + "(" + args.v + ");\n";
    ss.v = temp;
  }
}

void geraCodigo(ATRIBUTOS &ss, ATRIBUTOS s1, string opr, ATRIBUTOS s2){
  ss.t = buscaTipoOpr(opr, s1.t.base, s2.t.base);
  
  ss.v = geratemp(ss.t.base);
  if (ss.t.base == "S"){
    if (opr == "+"){
      ss.c = s1.c + s2.c + " strncpy(" + ss.v + ", " + s1.v + ", 255); strncat(" + ss.v + ", " + s2.v + ", 255);\n";
    }
  } //ver com o Prof 
  else if (s1.t.base == "S" || s2.t.base == "S") {
    if (opr == "="){
      ss.c = s1.c + s2.c + ss.v + " = (strncmp(" + s1.v + ", " + s2.v + ", 255) == 0);\n";
    }
    else if (opr == "~="){
      ss.c = s1.c + s2.c + ss.v + " = (strncmp(" + s1.v + ", " + s2.v + ", 255) != 0);\n";
    }
  } else {
    ss.c = s1.c + s2.c + ss.v + " = " + s1.v + " " + opr + " " + s2.v + ";\n";
  }
}

void geraCodigoVetor(ATRIBUTOS &ss, ATRIBUTOS id, ATRIBUTOS offset){
  string temp;

  if (id.t.d2 != 0)
    erroSemantico("Variavel " + id.v + " não é uma vetor. Indexe com duas dimensões");

  if (offset.t.base != "I")
    erroSemantico("Deslocamento de um vetor deve ser um Inteiro");
  
  ss.c = offset.c;
  ss.t = id.t;
  ss.v = id.v + "[" + offset.v + "]";
}

void geraCodigoVetor2(ATRIBUTOS &ss, ATRIBUTOS id, ATRIBUTOS offset1, ATRIBUTOS offset2){
  string temp;
  string temp2;

  if (offset1.t.base != "I")
    erroSemantico("Deslocamento de um vetor deve ser um Inteiro");
  
  temp = geratemp("I");

  ss.c = temp + " = " + offset1.v + " * " + toStr(id.t.d1) + ";\n" +
	temp + " = " + temp + " + " + offset2.v + ";\n";
  ss.t = id.t;
  ss.v = id.v + "[" + temp + "]";
}

void geraCodigoReturn(ATRIBUTOS &ss, ATRIBUTOS v){
  if (!emFuncao)
    erroSemantico("Return só é válido dentro de funcoes.");

  ss.c = v.c + "return " + v.v + ";\n";
}

void geraCodigoCin(ATRIBUTOS &ss, ATRIBUTOS v){
  ss.c = v.c + "cin >> " + v.v + ";\n";
}

string buscaTipoOpr(string opr, string t1, string t2){
  string tipo = tabelaResultados[t1 + opr + t2];

  if (tipo == "")
    erroSemantico("A operacao " + t1 + opr + t2 + " não suportada");

  return tipo;
}

void zeraAtributos(ATRIBUTOS &ss){
  ss = ATRIBUTOS();
}

string geraCodigoVariavel(string id, TIPO tipo){
  string aux = "";

  if (tipo.base == "I")
    aux += "int ";
  else if (tipo.base == "D")
    aux += "float ";
  else if (tipo.base == "B")
    aux += "int ";
  else if (tipo.base == "C")
    aux += "char ";
  else if (tipo.base == "S")
    aux += "char ";
  else
    erro("BUG!! Tipo não existente");

  aux += id;

  if (tipo.base == "S"){
    if (tipo.d1 != 0 && tipo.d2 == 0){
      aux += "[" + toStr(tipo.d1*255) + "]";
    } else if (tipo.d1 != 0 && tipo.d2 != 0){
      aux += "[" + toStr(tipo.d1*tipo.d2*255) + "]";
    } else {
      aux += "[255]";
    }
  } else {
    if (tipo.d1 != 0 && tipo.d2 == 0){
      aux += "[" + toStr(tipo.d1) + "]";
    } else if (tipo.d1 != 0 && tipo.d2 != 0){
      aux += "[" + toStr(tipo.d1*tipo.d2) + "]";
    }
  }

  aux += ";\n";

  return aux;
}

string geraCodigoVariavelLocal(string id, TIPO tipo){
  string aux = "";

  if (tipo.base == "I")
    aux += "int ";
  else if (tipo.base == "D")
    aux += "float ";
  else if (tipo.base == "B")
    aux += "int ";
  else if (tipo.base == "C")
    aux += "char ";
  else if (tipo.base == "S")
    aux += "char ";
  else
    erro("BUG!! Tipo não existente");

  aux += id;

  if (tipo.base == "S"){
    if (tipo.d1 != 0 && tipo.d2 == 0){
      aux += "[" + toStr(tipo.d1*255) + "]";
    } else if (tipo.d1 != 0 && tipo.d2 != 0){
      aux += "[" + toStr(tipo.d1*tipo.d2*255) + "]";
    } else {
      aux += "[255]";
    }
  } else {
    if (tipo.d1 != 0 && tipo.d2 == 0){
      aux += "[" + toStr(tipo.d1) + "]";
    } else if (tipo.d1 != 0 && tipo.d2 != 0){
      aux += "[" + toStr(tipo.d1*tipo.d2) + "]";
    }
  }

  return aux;
}

void geraCodigoIf(ATRIBUTOS &ss, ATRIBUTOS e, ATRIBUTOS cmds){
  string lbl_then = geraLabel("then");
  string lbl_fim = geraLabel("fim_if");

  if (e.t.base != "B")
    erroSemantico("Expressão usada no IF deve ser do tipo bool");

  ss.v = "";
  ss.c = e.c + "if ( " + e.v + ") goto " + lbl_then + ";\n"
	+ "goto " + lbl_fim + ";\n"
	+ lbl_then + ":\n"
	+ cmds.c
	+ lbl_fim + ":\n";
}

void geraCodigoIfElse(ATRIBUTOS &ss, ATRIBUTOS e, ATRIBUTOS cmds_t, ATRIBUTOS cmds_f){
  string lbl_then = geraLabel("then");
  string lbl_fim = geraLabel("fim_if");

  if (e.t.base != "B")
    erroSemantico("Expressão usada no IF deve ser do tipo bool");

  ss.v = "";
  ss.c = e.c + "if ( " + e.v + ") goto " + lbl_then + ";\n"
	+ cmds_f.c +
	+ "goto " + lbl_fim + ";\n"
	+ lbl_then + ":\n"
	+ cmds_t.c
	+ lbl_fim + ":\n";
}

void geraCodigoWhile(ATRIBUTOS &ss, ATRIBUTOS e, ATRIBUTOS cmds){
  string lbl_antes_while = geraLabel("antes_while");
  string lbl_fim_while = geraLabel("fim_while");

  string cond = geratemp("B");

  if (e.t.base != "B")
    erroSemantico("Expressão usada no 'Sempre que' deve ser do tipo bool");

  ss.v = "";
  ss.c = lbl_antes_while + ":\n" + e.c + cond + " = !" + e.v + ";\nif (" + cond + ")" + " goto " + lbl_fim_while + ";\n"
	+ cmds.c +
	+ "goto " + lbl_antes_while + ";\n"
	+ lbl_fim_while + ":\n";
	
}

void geraCodigoFor(ATRIBUTOS &ss, ATRIBUTOS var, ATRIBUTOS cond, ATRIBUTOS passo, ATRIBUTOS cmds){
  string lbl_antes_for = geraLabel("antes_for");
  string lbl_fim_for = geraLabel("fim_for");
  ATRIBUTOS inc = ATRIBUTOS();

  string cond2 = geratemp("B");

  if (var.t.base != "I")
    erroSemantico("Expressão usada na inicializacao Somatorio deve ser do tipo Inteiro");

  if (cond.t.base != "B")
    erroSemantico("Expressão usada na condicao do Somatorio deve ser do tipo Bool");

  if (passo.t.base != "I")
    erroSemantico("Expressão usada no passo do Somatorio deve ser do tipo Inteiro");

  geraCodigo(inc, var,"+", passo);

  ss.v = "";
  ss.c = ss.c + lbl_antes_for + ":\n" + cond.c + cond2 + " = !" + cond.v + ";\nif (" + cond2 + ")" + " goto " + lbl_fim_for + ";\n"
	+ cmds.c
	+ inc.c
	+ var.v + " = " + inc.v + ";\n"
	+ "goto " + lbl_antes_for + ";\n"
	+ lbl_fim_for + ":\n";
	
}

void geraCodigoFuncao(ATRIBUTOS &ss, ATRIBUTOS nome, ATRIBUTOS valores, TIPO tipo){
  string t = tipoToStr(tipo.base);
  string geraVariaveisFuncao(nome.v);

  ss.c = t + nome.v + "(" + valores.c + "){\n";
}

string tipoToStr( string tipo ){
  if (tipo == "I")
    return "int ";
  else if (tipo == "D")
    return "float ";
  else if (tipo == "B")
    return "int ";
  else if (tipo == "C")
    return "char ";
  else if (tipo == "S")
    return "char ";
  else
    return "void ";
}

void yyerror( const char* st ){
  cout << st << endl;
  cout << "Linha       : " << numLinha << endl;
  cout << "Coluna      : " << numCol << endl;
  cout << "Ultimo Token: " << ultimoToken << endl;
  cout << "Ultima Linha: " << ultimaLinha << endl;
  cout << "Ultima Regra: " << ultimaRegra << endl;
}

void erroSemantico( string msg ) {
  cout << msg << endl;
  cout << "Linha       : " << numLinha << endl;
  cout << "Coluna      : " << numCol << endl;
  cout << "Ultimo Token: " << ultimoToken << endl;
  cout << "Ultima Linha: " << ultimaLinha << endl;
  cout << "Ultima Regra: " << ultimaRegra << endl;
  exit(0);
}

void erro( string msg ) {
  cout << msg << endl;
  cout << "Linha       : " << numLinha << endl;
  cout << "Coluna      : " << numCol << endl;
  cout << "Ultimo Token: " << ultimoToken << endl;
  cout << "Ultima Linha: " << ultimaLinha << endl;
  cout << "Ultima Regra: " << ultimaRegra << endl;
  exit(0);
}

int main( int argc, char* argv[] )
{
  initTabelaResultados();
  yyparse();
}
